<?php
namespace app\common\model;

use Exception;
use think\facade\Db;
use think\facade\Log;

class CommonModel
{
    /**
     * @var string 表名
     */
    public static $tablename = '';
    public static $elastic = 0;

    /**
     * 获取 model 对应的表名
     * @author 贺强
     * @time   2022/6/30 10:42
     * @return string 当前表名
     */
    public static function tableName()
    {
        if (!empty(self::$tablename)) {
            return self::$tablename;
        }
        // 优先使用指定的表名
        if (!empty(static::$datatable)) {
            $prefix = env('database.prefix', '');
            // 判断指定的表名是否有表前缀
            if (strpos(static::$datatable, $prefix) === 0) {
                $prefix = '';
            }
            return $prefix . static::$datatable;
        }
        // 获取当前访问的类路径
        $table = static::class;
        // 截取类名
        $table = substr($table, strrpos($table, '\\') + 1);
        // 把类名转换为表名
        $table = substr($table, 0, strpos($table, 'Model'));
        $table = unhump($table);
        if ($table === 'common') {
            return false;
        }
        return env('database.prefix') . $table;
    }

    /**
     * 添加一条数据
     * @author 贺强
     * @time   2019-05-17 09:27:35
     * @param array $data 要添加的数据 ['name'=>'think', 'gender'=>'男', ……]
     * @param mixed $field 字段合法性检测
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|string
     */
    public static function add(array $data, $field = [], $fetchSql = false)
    {
        // 每个表都必须有ctime字段(int)，表示数据的添加时间
        if (empty($data['ctime'])) {
            $data['ctime'] = time();
        }
        $table = self::tableName();
        $model = Db::table($table);
        if (!empty($field)) {
            $model = $model->field($field);
        }
        if (!empty($fetchSql)) {
            $model = $model->fetchSql();
        }
        // 一般表都有id主键字段
        if (empty(static::$has_no_id) || static::$has_no_id !== 1) {
            if (empty($data['id'])) {
                $id = get_rid($table, '$&@#)');
                $data['id'] = $id;
            } else {
                $id = $data['id'];
            }
            try {
                $res = $model->insertGetId($data);
                if ($fetchSql) {
                    return $res;
                }
                if (!$res) {
                    return 0;
                }
            } catch (Exception $e) {
                $msg = $e->getMessage();
                // 如果当天的用户登录日志表不存在，则新建后再添加
                $flag = preg_match("/^SQLSTATE\[42S02]: Base table or view not found: 1146 Table '.*' doesn't exist$/", $msg);
                if ($flag) {
                    $date = date('Y-m-d');
                    $prefix = env('database.prefix', '');
                    self::execute("create table `$table` like `{$prefix}user_log`");
                    self::execute("ALTER TABLE `$table` COMMENT = '{$date}用户登录日志表';");
                    $res = $model->insertGetId($data);
                    if (!$res) {
                        Log::error($msg);
                        return 0;
                    }
                } else {
                    Log::error($msg);
                    return 0;
                }
            }
        } else {
            $res = $model->save($data);
            if ($fetchSql) {
                return $res;
            }
            if (!$res) {
                return 0;
            }
            $id = 1;
        }
        // 把数据添加到 elasticsearch
        if (!$fetchSql && ((static::$elastic ?? 0) === 1 || self::$elastic === 1)) {
            $index = 'es_index_' . $table;
            if (!EsModel::isExist($index)) {
                $properties = self::getProperties($table);
                EsModel::createIndex($index, $properties);
            }
            queue('app\\common\\job\\EsJob@add_es', ['index' => $index, 'data' => $data]);
            self::$elastic = 0;
        }
        return $id;
    }

    /**
     * 添加多条数据
     * @author 贺强
     * @time   2019-05-17 09:33:09
     * @param array $data 要添加的数据 [['name'=>'think', 'gender'=>'男', ……], ['name'=>'think', 'gender'=>'男', ……], ……]
     * @param integer $limit 每次插入的数量限制
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|string
     */
    public static function addArr(array $data, int $limit = 0, $fetchSql = false)
    {
        $table = self::tableName();
        // 给每条数据赋值id和添加时间
        foreach ($data as &$item) {
            if (empty(static::$has_no_id) || static::$has_no_id !== 1) {
                if (empty($item['id'])) {
                    $item['id'] = get_rid($table, '$&@#');
                }
            }
            if (empty($item['ctime'])) {
                $item['ctime'] = time();
            }
        }
        $model = Db::table($table);
        if (!empty($limit)) {
            $model = $model->limit($limit);
        }
        if (!empty($fetchSql)) {
            $model = $model->fetchSql();
        }
        $num = $model->insertAll($data);
        if (!$num) {
            return 0;
        }
        // 把数据添加到 elasticsearch
        if (!$fetchSql && ((static::$elastic ?? 0) === 1 || self::$elastic === 1)) {
            $index = 'es_index_' . $table;
            if (!EsModel::isExist($index)) {
                $properties = self::getProperties($table);
                EsModel::createIndex($index, $properties);
            }
            $new_data = [];
            foreach ($data as $item2) {
                $new_data[] = ['index' => ['_index' => $index, '_type' => '_doc', '_id' => $item2['id']], 'data' => $item2];
            }
            queue('app\\common\\job\\EsJob@save_es', $new_data);
            self::$elastic = 0;
        }
        return $num;
    }

    /**
     * 根据主键ID删除
     * @author 贺强
     * @time   2019-05-17 09:50:52
     * @param int|string|array $id 要删除的主键ID，1或[1, 2, 3, ……]
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|string 返回影响的行数
     */
    public static function delById($id, $fetchSql = false)
    {
        try {
            $table = self::tableName();
            $model = Db::table($table);
            if (!empty($fetchSql)) {
                $model = $model->fetchSql();
            }
            $num = $model->delete($id);
            // 删除 elasticsearch 中的数据
            $index = 'es_index_' . $table;
            if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && $num && !$fetchSql && EsModel::isExist($index)) {
                // EsModel::delFile('es_index_' . $table, $id);
                queue('app\\common\\job\\EsJob@del_es', ['index' => $index, 'id' => $id]);
                self::$elastic = 0;
            }
            return $num;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return 0;
        }
    }

    /**
     * 根据条件删除数据
     * @author 贺强
     * @time   2019-05-17 11:18:26
     * @param array|string $where 删除条件，[['id','>', 3],……]或SQL字符串
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|string 返回影响行数
     */
    public static function delByWhere($where, $fetchSql = false)
    {
        try {
            $table = self::tableName();
            $model = Db::table($table);
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (empty(static::$has_no_id) || static::$has_no_id !== 1) {
                $ids = self::getField('id', $where);
                if (count($ids) !== count($ids, 1)) {
                    $ids = array_column($ids, 'id');
                }
            }
            if (!empty($fetchSql)) {
                $model = $model->fetchSql();
            }
            $num = $model->delete();
            // 删除 elasticsearch 中的数据
            $index = 'es_index_' . $table;
            if ($num && ((static::$elastic ?? 0) === 1 || self::$elastic === 1) && !$fetchSql && !empty($ids) && EsModel::isExist($index)) {
                foreach ($ids as $id) {
                    // EsModel::delFile('es_index_' . $table, $id);
                    queue('app\\common\\job\\EsJob@del_es', ['index' => $index, 'id' => $id]);
                }
                self::$elastic = 0;
            }
            return $num;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return 0;
        }
    }

    /**
     * 根据条件修改数据
     * @author 贺强
     * @time   2019-05-17 11:21:13
     * @param array $data 要修改的数据 ['login_time'  => ['exp','now()'], 'name' => 'thinkphp', ……]
     * @param array|string $where 修改条件 [['id','>', 3],……]或SQL字符串
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|string       返回影响的行数
     */
    public static function modify(array $data, $where = '', $fetchSql = false)
    {
        try {
            // 每个表都必须有mtime字段(int)，表示数据的修改时间
            if (empty($data['mtime'])) {
                $data['mtime'] = time();
            }
            $table = self::tableName();
            $model = Db::table($table);
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            } elseif (!empty($data['id'])) {
                $where = ['id' => $data['id']];
                $model = $model->where($where);
            }
            if (!empty($fetchSql)) {
                $model = $model->fetchSql();
            }
            $num = $model->update($data);
            // 修改 elasticsearch 中的数据
            if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && !$fetchSql && $num && EsModel::isExist('es_index_' . $table)) {
                $list = self::getList($where, ['id']);
                $index = 'es_index_' . $table;
                // 循环单条更新
                foreach ($list as $item) {
                    $item = array_merge($item, $data);
                    queue('app\\common\\job\\EsJob@modify_es', ['index' => $index, 'data' => $item]);
                }
                // 批量更新 es
                // $new_data = [];
                // foreach ($list as $item) {
                //     $new_data[] = ['index' => ['_index' => $index, '_type' => '_doc', '_id' => $item['id']], 'data' => $data];
                // }
                // queue('app\\common\\job\\EsJob@save_es', $new_data);
                self::$elastic = 0;
            }
            return $num;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return 0;
        }
    }

    /**
     * 根据条件自增某字段的值
     * @author 贺强
     * @time   2019-05-17 11:28:15
     * @param string $field 要自增的字段
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param integer $step 自增量
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|string       返回影响的行数
     */
    public static function increment(string $field, $where = [], int $step = 1, $fetchSql = false)
    {
        try {
            if (is_array($where) && count($where) !== count($where, 1)) {
                foreach ($where as &$item) {
                    if ($item[1] === '!=') {
                        $item[1] = '<>';
                    }
                }
            }
            $table = self::tableName();
            $model = Db::table($table)
                ->where($where)
                ->inc($field, $step);
            if ($fetchSql) {
                $model = $model->fetchSql();
            }
            $num = $model->update();
            $index = 'es_index_' . $table;
            if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && !$fetchSql && $num && EsModel::isExist($index)) {
                $list = self::getList($where, ['id', $field]);
                foreach ($list as $item) {
                    $v = $item[$field] ?? 0;
                    $data = ['id' => $item['id'], "$field" => $v + $step];
                    queue('app\\common\\job\\EsJob@modify_es', ['index' => $index, 'data' => $data]);
                }
                self::$elastic = 0;
            }
            return $num;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return 0;
        }
    }

    /**
     * 根据条件自减某字段值
     * @author 贺强
     * @time   2019-05-17 11:31:24
     * @param string $field 要自减的字段
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param integer $step 自减量
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|string       返回影响的行数
     */
    public static function decrement(string $field, $where = [], int $step = 1, $fetchSql = false)
    {
        try {
            if (is_array($where) && count($where) !== count($where, 1)) {
                foreach ($where as &$item) {
                    if ($item[1] === '!=') {
                        $item[1] = '<>';
                    }
                }
            }
            $table = self::tableName();
            $model = Db::table($table)
                ->where($where)
                ->dec($field, $step);
            if ($fetchSql) {
                $model = $model->fetchSql($fetchSql);
            }
            $num = $model->update();
            $index = 'es_index_' . $table;
            if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && !$fetchSql && $num && EsModel::isExist($index)) {
                $list = self::getList($where, ['id', $field]);
                foreach ($list as $item) {
                    $v = $item[$field] ?? 0;
                    if ($v) {
                        $data = ['id' => $item['id'], "$field" => $v - $step];
                        queue('app\\common\\job\\EsJob@modify_es', ['index' => $index, 'data' => $data]);
                    }
                }
                self::$elastic = 0;
            }
            return $num;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return 0;
        }
    }

    /**
     * 根据条件获取某一(些)列的值
     * @author 贺强
     * @time   2019-05-28 15:30:02
     * @param array|string $field 要获取的字段名
     * @param array|string $where 条件 [['id','=',1],……]或SQL字符串
     * @param array|string $order 排序
     * @param integer $limit 查询多少条
     * @param mixed $is_es 是否先从 es 中取数据
     * @param mixed $fetchSql 是否打印SQL语句
     * @return array|string|void 返回某字段值的数组
     */
    public static function getField($field, $where = [], $order = '', int $limit = 0, $is_es = true, $fetchSql = false)
    {
        $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
        $enableDebug = self::ipIsInWhitelist();
        // 调试
        if ($enableDebug && $debug === 'field_w') {
            print_r($where);
            exit;
        } elseif ($enableDebug && $debug === 'field_var_w') {
            var_dump($where);
            exit;
        }
        $table = self::tableName();
        if ($enableDebug && $debug === 'field_exists') {
            var_dump($table, EsModel::isExist('es_index_' . $table));
            exit;
        }
        if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && $is_es && !$fetchSql && EsModel::isExist('es_index_' . $table)) {
            $param = self::getElasticTerm($where);
            $body = [];
            if (!empty($param)) {
                $body['bool'] = $param;
            }
            $page = 1;
            $size = 10000;
            if (!empty($limit)) {
                if (is_numeric($limit)) {
                    $size = $limit;
                } elseif (strpos($limit, ',')) {
                    [$page, $size] = explode(',', $limit);
                }
            }
            $list = EsModel::getData('es_index_' . $table, $field, $body, $order, $page, $size);
            if ($enableDebug && $debug === 'es_field_list') {
                print_r($list);
                exit;
            } elseif ($enableDebug && $debug === 'es_var_field_list') {
                var_dump($list);
                exit;
            }
            if (!empty($list['list'])) {
                $list = array_column($list['list'], '_source');
                if ($enableDebug && $debug === 'es_field_source') {
                    print_r($list);
                    exit;
                }
                if (is_array($field) && count($field) === 1) {
                    $field = $field[0];
                    $list = array_column($list, $field);
                } elseif (is_string($field) && strpos($field, ',')) {
                    $list = array_column($list, $field);
                }
                return $list;
            }
        }
        $model = Db::table($table);
        if (!empty($where)) {
            if (is_array($where) && count($where) !== count($where, 1)) {
                foreach ($where as &$item) {
                    if ($item[1] === '!=') {
                        $item[1] = '<>';
                    }
                }
            }
            $model = $model->where($where);
        }
        if (!empty($order)) {
            $model = $model->order($order);
        }
        if (!empty($limit)) {
            $model = $model->limit($limit);
        }
        if (!empty($fetchSql)) {
            $model = $model->fetchSql();
        }
        $column = $model->column($field);
        if ($enableDebug && $debug === 'field_r') {
            print_r($column);
            exit;
        } elseif ($enableDebug && $debug === 'field_var_r') {
            var_dump($column);
            exit;
        }
        return $column;
    }

    /**
     * 根据条件获取某一列符合条件的第一条值
     * @author 贺强
     * @time   2019-06-01 14:45:27
     * @param array|string $field 列名
     * @param array|string $where 条件 [['id','=',1],……]或SQL字符串
     * @param array|string $order 按某(些)字段排序 ['id'=>'desc']或'id desc'
     * @param mixed $is_es 是否先从 es 中取数据
     * @param mixed $fetchSql 是否打印 SQL 语句
     * @return mixed|string|void       返回获取的值
     */
    public static function getFieldValue($field, $where = [], $order = '', $is_es = true, $fetchSql = false)
    {
        // 调试
        $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
        $enableDebug = self::ipIsInWhitelist();
        if ($order === 'debug') {
            $order = '';
            $debug = '';
        }
        if ($enableDebug && $debug === 'field_value_w') {
            print_r($where);
            exit;
        } elseif ($enableDebug && $debug === 'field_value_var_w') {
            var_dump($where);
            exit;
        }
        $table = self::tableName();
        if ($enableDebug && $debug === 'field_value_exists') {
            var_dump($table, EsModel::isExist('es_index_' . $table));
            exit;
        }
        if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && $is_es && !$fetchSql && EsModel::isExist('es_index_' . $table)) {
            if (is_array($where) && !empty($where['id'])) {
                $data = EsModel::getFile('es_index_' . $table, $where['id']);
                if (isset($data['_source'][$field])) {
                    return $data['_source'][$field];
                }
            }
            $param = self::getElasticTerm($where);
            $body = [];
            if (!empty($param)) {
                $body['bool'] = $param;
            }
            self::$elastic = 0;
            $list = EsModel::getData('es_index_' . $table, $field, $body, $order, 1, 1);
            if ($enableDebug && $debug === 'es_field_value_list') {
                print_r($list);
                exit;
            } elseif ($enableDebug && $debug === 'es_var_field_value_list') {
                var_dump($list);
                exit;
            }
            if (isset($list['list'][0]['_source'][$field])) {
                return $list['list'][0]['_source'][$field];
            }
        }
        $model = Db::table($table);
        if (!empty($where)) {
            if (is_array($where) && count($where) !== count($where, 1)) {
                foreach ($where as &$item) {
                    if ($item[1] === '!=') {
                        $item[1] = '<>';
                    }
                }
            }
            $model = $model->where($where);
        }
        if (!empty($order)) {
            $model = $model->order($order);
        }
        if (!empty($fetchSql)) {
            $model = $model->fetchSql();
        }
        $value = $model->value($field);
        if ($enableDebug && $debug === 'field_value_r') {
            print_r($value);
            exit;
        } elseif ($enableDebug && $debug === 'field_value_var_r') {
            var_dump($value);
            exit;
        }
        return $value;
    }

    /**
     * 根据条件获取总数量
     * @author 贺强
     * @time   2019-05-17 11:35:19
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param array|string $field 要统计数量的字段
     * @param string $group 对结果进行分组
     * @param mixed $is_es 是否先从 es 中取数据
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|mixed|string|void 返回总数量
     */
    public static function getCount($where = '', $field = '*', string $group = '', $is_es = true, $fetchSql = false)
    {
        try {
            $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
            $enableDebug = self::ipIsInWhitelist();
            if ($group === 'debug') {
                $group = '';
                $debug = '';
            }
            // 调试
            if ($enableDebug && $debug === 'count_w') {
                print_r($where);
                exit;
            } elseif ($enableDebug && $debug === 'count_var_w') {
                var_dump($where);
                exit;
            }
            $table = self::tableName();
            if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && $is_es && !$fetchSql && EsModel::isExist('es_index_' . $table)) {
                $param = self::getElasticTerm($where);
                $body = [];
                if (!empty($param)) {
                    $body['bool'] = $param;
                }
                self::$elastic = 0;
                $count = EsModel::getCount('es_index_' . $table, $body, $group);
                if ($enableDebug && $debug === 'es_count') {
                    print_r($count);
                    exit;
                } elseif ($enableDebug && $debug === 'es_var_count') {
                    var_dump($count);
                    exit;
                }
                if (!empty($count['count'])) {
                    return $count['count'];
                }
            }
            $model = Db::table($table);
            if (empty($field)) {
                $field = '*';
            }
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (!empty($group)) {
                $model = $model->group($group);
            }
            if (!empty($fetchSql)) {
                $model = $model->fetchSql();
            }
            $count = $model->count($field);
            if ($enableDebug && $debug === 'count_r') {
                var_dump($count);
                exit;
            }
            return $count;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return 0;
        }
    }

    /**
     * 根据条件查询一条数据
     * @author 贺强
     * @time   2019-05-17 11:37:39
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param array|boolean $field 要查询的字段
     * @param array|string $order 按某(些)字段排序 ['id'=>'desc']或'id desc'
     * @param mixed $is_es 是否先从 es 中取数据
     * @param mixed $fetchSql 是否打印SQL语句
     * @return array|mixed|string|\think\db\Fetch|Db|\think\Model|void|null 返回查询结果
     */
    public static function getModel($where = '', $field = true, $order = null, $is_es = true, $fetchSql = false)
    {
        try {
            $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
            $enableDebug = self::ipIsInWhitelist();
            // 调试
            if ($enableDebug && $debug === 'model_w') {
                print_r($where);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_w') {
                var_dump($where);
                exit;
            }
            $table = self::tableName();
            if ($enableDebug && $debug === 'model_exists') {
                var_dump($table, EsModel::isExist('es_index_' . $table));
                exit;
            }
            if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && $is_es && !$fetchSql && EsModel::isExist('es_index_' . $table)) {
                self::$elastic = 0;
                if (is_array($where) && !empty($where['id'])) {
                    $data = EsModel::getFile('es_index_' . $table, $where['id'], $field);
                    if ($enableDebug && $debug === 'model_source') {
                        print_r($data);
                        exit;
                    }
                    if (!empty($data['_source'])) {
                        return $data['_source'];
                    }
                }
                $param = self::getElasticTerm($where);
                $body = [];
                if (!empty($param)) {
                    $body['bool'] = $param;
                }
                $list = EsModel::getData('es_index_' . $table, $field, $body, '', 1, 1);
                if ($enableDebug && $debug === 'es_model_list') {
                    print_r($list);
                    exit;
                } elseif ($enableDebug && $debug === 'es_var_model_list') {
                    var_dump($list);
                    exit;
                }
                if (($list['list'][0]['_source'] ?? false)) {
                    if ($enableDebug && $debug === 'es_model') {
                        print_r($list['list'][0]['_source']);
                        exit;
                    } elseif ($enableDebug && $debug === 'es_var_model') {
                        var_dump($list['list'][0]['_source']);
                        exit;
                    }
                    return $list['list'][0]['_source'];
                }
            }
            $model = Db::table($table);
            if (!empty($order)) {
                $model = $model->order($order);
            }
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (!empty($field)) {
                $model = $model->field($field);
            }
            if (!empty($fetchSql)) {
                $model = $model->fetchSql();
            }
            $data = $model->find();
            if ($enableDebug && $debug === 'model_r') {
                print_r($data);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_r') {
                var_dump($data);
                exit;
            }
            return $data;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return [];
        }
    }

    /**
     * 根据条件查询数据集
     * @author 贺强
     * @time   2019-05-17 11:42:16
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param array|boolean $field 要查询的字段
     * @param array|string|int $limit 分页查询 '1,10'、[1,10]、10 页码和每页条数
     * @param array|string $order 按某(些)字段排序 ['id'=>'desc']或'id desc'
     * @param string $group 对结果集进行分组
     * @param string $having 配合group使用的聚合筛选条件
     * @param string $distinct 查询去重字段
     * @param mixed $is_es 是否先从 es 中取数据
     * @param mixed $fetchSql 是否打印SQL语句
     * @return array|string|\think\Collection|\think\db\Fetch[]|Db[] 返回查询结果(数组)
     */
    public static function getList($where = null, $field = true, $limit = null, $order = null, string $group = '', string $having = '', string $distinct = '', $is_es = true, $fetchSql = false)
    {
        try {
            $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
            // 调试
            $enableDebug = self::ipIsInWhitelist();
            if ($enableDebug && $debug === 'list_w') {
                print_r($where);
                exit;
            } elseif ($debug === 'list_var_w') {
                var_dump($where);
                exit;
            }
            $table = self::tableName();
            if ($debug === 'list_exists' || $debug === $table . '_exists' && $enableDebug) {
                var_dump($table, EsModel::isExist('es_index_' . $table));
                exit;
            }
            // 先从 elasticsearch 中取数据
            if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && $is_es && !$fetchSql && EsModel::isExist('es_index_' . $table)) {
                $param = self::getElasticTerm($where);
                $body = [];
                if (!empty($param)) {
                    $body['bool'] = $param;
                }
                $page = 1;
                $size = 10000;
                if (is_array($limit) && count($limit) === 2 && $limit[0] > 0 && $limit[1] > 0) {
                    [$page, $size] = $limit;
                } elseif (is_string($limit)) {
                    if (strpos($limit, ',') !== false) {
                        $limit = explode(',', $limit);
                        if (count($limit) === 2 && $limit[0] > 0 && $limit[1] > 0) {
                            [$page, $size] = $limit;
                        }
                    } elseif (is_numeric($limit)) {
                        $size = $limit;
                    }
                }
                self::$elastic = 0;
                $list = EsModel::getData('es_index_' . $table, $field, $body, $order, $page, $size, $group, $distinct);
                if ($enableDebug && $debug === 'es_list') {
                    print_r($list);
                    exit;
                } elseif ($enableDebug && $debug === 'es_var_list') {
                    var_dump($list);
                    exit;
                }
                if (!empty($list['list'])) {
                    $list = array_column($list['list'], '_source');
                    if ($enableDebug && $debug === 'es_source') {
                        print_r($list);
                        exit;
                    }
                    return $list;
                }
            }
            $model = Db::table($table)->field($field);
            if (!empty($limit)) {
                if (is_array($limit)) {
                    if (count($limit) === 2 && $limit[0] > 0 && $limit[1] > 0) {
                        $model = $model->page($limit[0], $limit[1]);
                    }
                } elseif (is_string($limit) && strpos($limit, ',')) {
                    $limit = explode(',', $limit);
                    if (count($limit) === 2 && $limit[0] > 0 && $limit[1] > 0) {
                        [$page, $size] = $limit;
                        $model = $model->page($page, $size);
                    }
                } elseif (is_numeric($limit)) {
                    $model = $model->page($limit);
                }
            }
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (!empty($order)) {
                $model = $model->order($order);
            }
            if (!empty($group)) {
                $model = $model->group($group);
            }
            if (!empty($having)) {
                $model = $model->having($having);
            }
            if (!empty($distinct)) {
                $model->distinct($distinct);
            }
            if ($fetchSql) {
                return $model->fetchSql()->select();
            }
            $list = $model->select()->toArray();
            if ($enableDebug && $debug === 'list_r') {
                print_r($list);
                exit;
            } elseif ($enableDebug && $debug === 'list_var_r') {
                var_dump($list);
                exit;
            }
            return $list;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            $prefix = env('database.prefix', '');
            if (preg_match("/.*{$prefix}user_log_.*/", $e->getMessage())) {
                return ['code' => 333, 'message' => "{$prefix}user_log_" . date('Ymd') . "表不存在"];
            }
            return [];
        }
    }

    /**
     * 根据条件查询数据集
     * @author 贺强
     * @time   2019-05-17 11:49:11
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param array|boolean $field 要查询的字段
     * @param string $limit 分页查询 '1,10' 从哪一条开始查多少条件
     * @param array|string $order 按某(些)字段排序 ['id'=>'desc']或'id desc'
     * @param string $group 对查询结果进行分组
     * @param string $having 配合group使用的聚合筛选条件
     * @param string $distinct 查询去重字段
     * @param mixed $is_es 是否先从 es 中取数据
     * @param mixed $fetchSql 是否打印SQL语句
     * @return array|string|\think\Collection|\think\db\Fetch[]|Db[] 返回查询结果集
     */
    public static function getLimitList($where, $field = true, $limit = 0, $order = 'id', string $group = '', string $having = '', string $distinct = '', $is_es = true, $fetchSql = false)
    {
        try {
            $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
            $enableDebug = self::ipIsInWhitelist();
            // 调试
            if ($enableDebug && $debug === 'model_w') {
                print_r($where);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_w') {
                var_dump($where);
                exit;
            }
            $table = self::tableName();
            $page = 1;
            $pagesize = 20;
            if (!empty($limit) && strpos($limit, ',')) {
                [$page, $pagesize] = explode(',', $limit);
            } elseif (is_numeric($limit)) {
                $pagesize = $limit;
            }
            // 先从 elasticsearch 中取数据
            if (((static::$elastic ?? 0) === 1 || self::$elastic === 1) && $is_es && !$fetchSql && EsModel::isExist('es_index_' . $table)) {
                $param = self::getElasticTerm($where);
                $body = [];
                if (!empty($param)) {
                    $body['bool'] = $param;
                }
                self::$elastic = 0;
                $list = EsModel::getData('es_index_' . $table, $field, $body, $order, $page, $pagesize, $group, $distinct);
                if ($enableDebug && $debug === 'es_list') {
                    print_r($list);
                    exit;
                } elseif ($enableDebug && $debug === 'es_var_list') {
                    var_dump($list);
                    exit;
                }
                if (!empty($list['list'])) {
                    $list = array_column($list['list'], '_source');
                    if ($enableDebug && $debug === 'es_source') {
                        print_r($list);
                    }
                    return $list;
                }
            }
            $model = Db::table($table);
            if (empty($field)) {
                $field = true;
            }
            $model = $model->field($field);
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (!empty($limit)) {
                $offset = ($page - 1) * $pagesize;
                $model = $model->limit($offset, $pagesize);
            }
            if (!empty($order)) {
                $model = $model->order($order);
            }
            if (!empty($group)) {
                $model = $model->group($group);
            }
            if (!empty($having)) {
                $model = $model->having($having);
            }
            if (!empty($distinct)) {
                $model->distinct($distinct);
            }
            if (!empty($fetchSql)) {
                return $model->fetchSql()->select();
            }
            $list = $model->select()->toArray();
            if ($enableDebug && $debug === 'model_r') {
                print_r($list);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_r') {
                var_dump($list);
                exit;
            }
            return $list;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return [];
        }
    }

    /**
     * 联合查询总数
     * @author 贺强
     * @time   2019-05-17 13:49:57
     * @param array $join 联表查询[['think_card c','a.card_id=c.id'], ……]
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param string $group 对查询结果进行分组
     * @param string $having 配合group使用的聚合筛选条件
     * @param string $alias 设置当前数据表的别名，默认为 a
     * @param mixed $fetchSql 是否打印SQL语句
     * @return int|string|void 返回总数量
     */
    public static function getJoinCount(array $join, $where = '', string $group = '', string $having = '', string $alias = 'a', $fetchSql = false)
    {
        try {
            $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
            $enableDebug = self::ipIsInWhitelist();
            // 调试
            if ($enableDebug && $debug === 'model_w') {
                print_r($where);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_w') {
                var_dump($where);
                exit;
            }
            $model = Db::table(self::tableName())->alias($alias);
            if (count($join) == count($join, 1)) {
                $model = $model->join($join[0], $join[1]);
            } else {
                foreach ($join as $item) {
                    $model = $model->join($item[0], $item[1]);
                }
            }
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (!empty($group)) {
                $model = $model->group($group);
            }
            if (!empty($having)) {
                $model = $model->having($having);
            }
            if (!empty($fetchSql)) {
                $model = $model->fetchSql();
            }
            $count = $model->count();
            if ($enableDebug && $debug === 'model_r') {
                var_dump($count);
                exit;
            }
            return $count;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return 0;
        }
    }

    /**
     * 联表查询
     * @author 贺强
     * @time   2019-05-17 13:54:07
     * @param array $join 联表查询 [['think_card c','a.card_id=c.id'], ……]
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param array|boolean $field 要查询的字段
     * @param string $limit 分页查询 '1,10' 页码和每页多少条
     * @param array|string $order 按某(些)字段排序 ['id'=>'desc']或'id desc'
     * @param string $group 对查询结果进行分组
     * @param string $having 配合group使用的聚合筛选条件
     * @param string $alias 设置当前表的别名，默认为 a
     * @param mixed $fetchSql 是否打印SQL语句
     * @return array|string|\think\Collection|\think\db\Fetch[]|Db[]|void 返回查询结果集
     */
    public static function getJoinList(array $join, $where = '', $field = '*', string $limit = '', $order = '', string $group = '', string $having = '', string $alias = 'a', $fetchSql = false)
    {
        try {
            $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
            $enableDebug = self::ipIsInWhitelist();
            // 调试
            if ($enableDebug && $debug === 'model_w') {
                print_r($where);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_w') {
                var_dump($where);
                exit;
            }
            $model = Db::table(self::tableName())->alias($alias)->field($field);
            if (count($join) == count($join, 1)) {
                $model = $model->join($join[0], $join[1]);
            } else {
                foreach ($join as $item) {
                    $model = $model->join($item[0], $item[1]);
                }
            }
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (is_array($limit)) {
                $model = $model->page($limit[0], $limit[1]);
            } elseif (is_numeric($limit)) {
                $model = $model->page($limit);
            }
            if (!empty($order)) {
                $model = $model->order($order);
            }
            if (!empty($group)) {
                $model = $model->group($group);
            }
            if (!empty($having)) {
                $model = $model->having($having);
            }
            if (!empty($fetchSql)) {
                return $model->fetchSql()->select();
            }
            $list = $model->select()->toArray();
            if ($enableDebug && $debug === 'model_r') {
                print_r($list);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_r') {
                var_dump($list);
                exit;
            }
            return $list;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return [];
        }
    }

    /**
     * 连表查询一条数据
     * @author 贺强
     * @time   2019-06-05 17:47:45
     * @param array $join 联表查询 [['think_card c','a.card_id=c.id'], ……]
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param boolean $field 要查询的字段
     * @param array|string $order 按某(些)字段排序 ['id'=>'desc']或'id desc'
     * @param string $alias 表别名
     * @param mixed $fetchSql 是否打印 SQL 语句
     * @return array|mixed|string|\think\db\Fetch|Db|\think\Model|void|null 返回查询结果
     */
    public static function getJoinModel(array $join, $where = '', $field = '*', $order = '', string $alias = 'a', $fetchSql = false)
    {
        try {
            $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
            $enableDebug = self::ipIsInWhitelist();
            // 调试
            if ($enableDebug && $debug === 'model_w') {
                print_r($where);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_w') {
                var_dump($where);
                exit;
            }
            $model = Db::table(self::tableName())->alias($alias)->field($field);
            if (count($join) == count($join, 1)) {
                $model = $model->join($join[0], $join[1]);
            } else {
                foreach ($join as $item) {
                    $model = $model->join($item[0], $item[1]);
                }
            }
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (!empty($order)) {
                $model = $model->order($order);
            }
            if (!empty($fetchSql)) {
                $model = $model->fetchSql();
            }
            $data = $model->find();
            if ($enableDebug && $debug === 'model_r') {
                print_r($data);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_r') {
                var_dump($data);
                exit;
            }
            return $data;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return [];
        }
    }

    /**
     * Union查询
     * @author 贺强
     * @time   2019-05-17 13:58:51
     * @param array $union 联合的SQL语句 ['SELECT name FROM table1',……]
     * @param array|string $where 条件 [['id','>', 3],……]或SQL字符串
     * @param array|boolean $field 要查询的字段
     * @param mixed $is_all 是否是union all操作，默认不是
     * @param string $alias 当前表别名，默认为 a
     * @param mixed $fetchSql 是否打印SQL语句
     * @return array|string|\think\Collection|\think\db\Fetch[]|Db[]|void 返回查询结果集
     */
    public static function getUnionList(array $union, $where = '', $field = true, $is_all = false, string $alias = 'a', $fetchSql = false)
    {
        try {
            $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
            $enableDebug = self::ipIsInWhitelist();
            // 调试
            if ($enableDebug && $debug === 'model_w') {
                print_r($where);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_w') {
                var_dump($where);
                exit;
            }
            $model = Db::table(self::tableName())->alias($alias)->field($field)->union($union, $is_all);
            if (!empty($where)) {
                if (is_array($where) && count($where) !== count($where, 1)) {
                    foreach ($where as &$item) {
                        if ($item[1] === '!=') {
                            $item[1] = '<>';
                        }
                    }
                }
                $model = $model->where($where);
            }
            if (!empty($fetchSql)) {
                return $model->fetchSql()->select();
            }
            $list = $model->select()->toArray();
            if ($enableDebug && $debug === 'model_r') {
                print_r($list);
                exit;
            } elseif ($enableDebug && $debug === 'model_var_r') {
                var_dump($list);
                exit;
            }
            return $list;
        } catch (Exception $e) {
            Log::error($e->getMessage());
            return [];
        }
    }

    /**
     * 执行原生 SQL 查询
     * @author 贺强
     * @time   2016-11-16 15:55:46
     * @param string $sql 要执行的 SQL 语句
     * @return mixed       返回查询结果
     */
    public static function query(string $sql)
    {
        $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
        $enableDebug = self::ipIsInWhitelist();
        // 调试
        if ($enableDebug && $debug === 'query_w') {
            var_dump($sql);
            exit;
        }
        $list = Db::query($sql);
        if ($enableDebug && $debug === 'query_r') {
            print_r($list);
            exit;
        } elseif ($enableDebug && $debug === 'query_var_r') {
            var_dump($list);
            exit;
        }
        return $list;
    }

    /**
     * 执行原生 SQL 增、删、改
     * @author 贺强
     * @time   2019-08-08 10:05:19
     * @param string $sql 要执行的 SQL 语句
     * @return mixed      返回执行影响的行数
     */
    public static function execute(string $sql)
    {
        $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
        $enableDebug = self::ipIsInWhitelist();
        // 调试
        if ($enableDebug && $debug === 'execute_w') {
            var_dump($sql);
            exit;
        }
        $res = Db::execute($sql);
        if ($enableDebug && $debug === 'execute_r') {
            var_dump($res);
            exit;
        }
        return $res;
    }

    /**
     * 根据表名获取表字段及属性
     * @author 贺强
     * @time   2022/3/19 14:48
     * @param string $tableName 表名
     * @return array|void
     */
    public static function getProperties(string $tableName = '')
    {
        $properties = [];
        $fieldInfo = self::query("DESC `$tableName`");
        $debug = $_GET['debug'] ?? ($_COOKIE['debug'] ?? '');
        $enableDebug = self::ipIsInWhitelist();
        if ($enableDebug && $debug === 'fieldInfo') {
            print_r($fieldInfo);
            exit;
        } elseif ($enableDebug && $debug === 'fieldInfo_var') {
            var_dump($fieldInfo);
            exit;
        }
        foreach ($fieldInfo as $field) {
            $type = self::getFieldType($field['Type']);
            $properties[$field['Field']] = [
                'type' => $type,
            ];
            if ($type === 'text') {
                $properties[$field['Field']]['fields'] = [
                    'keyword' => [
                        'type' => 'keyword',
                        'ignore_above' => 256,
                    ],
                ];
            }
        }
        return $properties;
    }

    /**
     * 获取 mysql 字段类型对应的 es 字段类型
     * @author 贺强
     * @time   2022/3/19 15:54
     * @param string $type mysql 字段类型
     * @return string 返回类型映射
     */
    public static function getFieldType(string $type = '')
    {
        $types = [
            'char' => 'keyword',
            'varchar' => 'keyword',
            'double' => 'double',
            'float' => 'float',
            'decimal' => 'float',
            'text' => 'text',
            'longtext' => 'text',
            'bigint' => 'integer',
            'int' => 'integer',
            'smallint' => 'integer',
            'tinyint' => 'integer',
            'datetime' => 'date',
            'date' => 'date',
            'json' => 'keyword',
        ];
        $start = strpos($type, '(');
        $type = substr($type, 0, $start);
        return $types[$type] ?? 'keyword';
    }

    /**
     * 获取 elasticsearch 查询条件
     * @author 贺强
     * @time   2022/3/19 18:17
     * @param mixed $where 条件数组
     * @return array
     */
    public static function getElasticTerm($where)
    {
        if (empty($where)) {
            return [];
        }
        $param = [];
        if (count($where) === count($where, 1)) {
            $keys = array_keys($where);
            foreach ($keys as $key) {
                if (strpos($key, '|')) {
                    $should = [];
                    foreach (explode('|', $key) as $sd) {
                        $should[] = ['term' => [$sd => $where[$key]]];
                    }
                    $param['filter'][] = ['bool' => ['should' => $should]];
                } else {
                    $param['filter'][] = ['term' => [$key => $where[$key]]];
                }
            }
        } else {
            foreach ($where as $w) {
                if (strpos($w[0], '|') !== false) {
                    $should = [];
                    foreach (explode('|', $w[0]) as $sd) {
                        if ($w[1] === '=') {
                            $should[] = ['term' => [$sd => $w[2]]];
                        } elseif ($w[1] === '>' || $w[1] === 'gt') {
                            $should[] = ['range' => [$sd => ['gt' => $w[2]]]];
                        } elseif ($w[1] === '<' || $w[1] === 'lt') {
                            $should[] = ['range' => [$sd => ['lt' => $w[2]]]];
                        } elseif ($w[1] === '>=' || $w[1] === 'gte') {
                            $should[] = ['range' => [$sd => ['gte' => $w[2]]]];
                        } elseif ($w[1] === '<=' || $w[1] === 'lte') {
                            $should[] = ['range' => [$sd => ['lte' => $w[2]]]];
                        } elseif ($w[1] === 'like') {
                            $v = str_replace('%', '*', $w[2]);
                            $should[] = ['wildcard' => ["$sd.keyword" => "$v"]];
                            $should[] = ['wildcard' => [$sd => "$v"]];
                        } elseif ($w[1] === 'in') {
                            $should[] = ['terms' => [$sd => $w[2]]];
                        }
                    }
                    $param['filter'][] = [
                        'bool' => [
                            'should' => $should,
                        ],
                    ];
                } elseif ($w[1] === '=') {
                    $param['filter'][] = ['term' => [$w[0] => $w[2]]];
                } elseif ($w[1] === '<>' || $w[1] === '!=') {
                    $param['must_not'][] = ['term' => [$w[0] => $w[2]]];
                } elseif ($w[1] === 'between') {
                    $param['filter'][] = ['range' => [$w[0] => ['gte' => $w[2][0], 'lte' => $w[2][1]]]];
                } elseif ($w[1] === '>' || $w[1] === 'gt') {
                    $param['filter'][] = ['range' => [$w[0] => ['gt' => $w[2]]]];
                } elseif ($w[1] === '<' || $w[1] === 'lt') {
                    $param['filter'][] = ['range' => [$w[0] => ['lt' => $w[2]]]];
                } elseif ($w[1] === '>=' || $w[1] === 'gte') {
                    $param['filter'][] = ['range' => [$w[0] => ['gte' => $w[2]]]];
                } elseif ($w[1] === '<=' || $w[1] === 'lte') {
                    $param['filter'][] = ['range' => [$w[0] => ['lte' => $w[2]]]];
                } elseif ($w[1] === 'like') {
                    $v = str_replace('%', '*', $w[2]);
                    $param['filter'][] = [
                        'bool' => [
                            'should' => [
                                ['wildcard' => ["$w[0].keyword" => "$v"]],
                                ['wildcard' => [$w[0] => "$v"]],
                            ],
                        ],
                    ];
                } elseif ($w[1] === 'in') {
                    if (is_string($w[2])) {
                        $w[2] = explode(',', $w[2]);
                    }
                    $param['filter'][] = ['terms' => [$w[0] => $w[2]]];
                    // if (is_string($w[2])) {
                    //     $w[2] = explode(',', $w[2]);
                    // }
                    // $should = [];
                    // foreach ($w[2] as $v) {
                    //     $should[] = ['term' => [$w[0] => $v]];
                    // }
                    // $param['filter'][] = $should;
                } elseif ($w[1] === 'not in') {
                    if (is_string($w[2])) {
                        $w[2] = explode(',', $w[2]);
                    }
                    $param['must_not'][] = ['terms' => [$w[0] => $w[2]]];
                }
            }
        }
        return $param;
    }

    /**
     * 事务处理
     * @author 贺强
     * @time   2023/3/31 9:11
     * @param callable $fn 要加入事务处理的函数体
     * @return mixed
     */
    public static function transaction(callable $fn)
    {
        Db::startTrans();
        $flag = $fn();
        if ($flag) {
            Db::commit();
        } else {
            Db::rollback();
        }
        return $flag;
    }

    /**
     * 判断IP是否在白名单
     * @author 贺强
     * @time   2024/6/11 21:12
     * @return bool
     */
    private static function ipIsInWhitelist()
    {
        $isIp = env('app.isipdebug', false);
        if (!$isIp) {
            return true;
        }
        $ip = get_client_ip();
        $prefix = env('database.prefix', '');
        $sql = "SELECT count(*) c FROM `{$prefix}admin_ips` WHERE `ip`='$ip'";
        $res = Db::query($sql);
        if ($res !== null && $res[0]['c'] >= 1) {
            return true;
        }
        return false;
    }
}
